package com.algo.QFS;

/**
 * @Author: guoxing
 * @Email: gx_study2022@163.com
 * @Description: https://blog.csdn.net/weixin_37980595/article/details/125443270?spm=1001.2014.3001.5502
 * @Date: 2024/6/25 9:10
 * @ClassName: FlexibleShopSchedulingProblemDemo
 **/
//import ilog.concert.IloException;
//import ilog.concert.IloNumVar;
//import ilog.cplex.IloCplex;

import java.util.ArrayList;
import java.util.Random;
//启发方法求解，遗传算法求解，cplex求解
public class FlexibleShopSchedulingProblemDemo {
    //实验数据
    class Data{
        //工件编号
        int[] workId={1,2,3,4,5,6,7,8};
        //工序
        int[][] workProcess={
                {1,2,3},
                {1,2,3,4,5,6},
                {1,2,3,4},
                {1,2,3},
                {1,2},
                {1,2,3},
                {1,2,3,4},
                {1,2,3}
        };
        //工件交货期
        double[] deliveryDate={60,100,120,80,60,80,Double.MAX_VALUE,100};
        //设备编号
        int[] deviceId={1,2,3,4,5,6,7,8};
        //加工时间
        double[][][] ProcessTime={
                //工件1
                {
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,12,Double.MAX_VALUE,10,9,Double.MAX_VALUE},
                        {17,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,17,10,Double.MAX_VALUE,15},
                        {Double.MAX_VALUE,24,Double.MAX_VALUE,11,Double.MAX_VALUE,Double.MAX_VALUE,10,Double.MAX_VALUE}
                },
                //工件2
                {
                        {Double.MAX_VALUE,Double.MAX_VALUE,11,10,21,14,Double.MAX_VALUE,17},
                        {8,12,Double.MAX_VALUE,Double.MAX_VALUE,19,11,Double.MAX_VALUE,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,15,Double.MAX_VALUE,21,25,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,18,Double.MAX_VALUE,9,Double.MAX_VALUE,},
                        {12,15,Double.MAX_VALUE,14,9,Double.MAX_VALUE,10,Double.MAX_VALUE},
                        {Double.MAX_VALUE,9,Double.MAX_VALUE,7,10,8,Double.MAX_VALUE,Double.MAX_VALUE}
                },
                //工件3
                {
                        {Double.MAX_VALUE,14,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,17,Double.MAX_VALUE},
                        {Double.MAX_VALUE,23,23,17,Double.MAX_VALUE,18,Double.MAX_VALUE,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,20,9,22,Double.MAX_VALUE,Double.MAX_VALUE,21},
                        {7,Double.MAX_VALUE,10,Double.MAX_VALUE,8,11,9,Double.MAX_VALUE}
                },
                //工件4
                {
                        {Double.MAX_VALUE,18,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,17,18,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,10,Double.MAX_VALUE,Double.MAX_VALUE,12,Double.MAX_VALUE},
                        {Double.MAX_VALUE,8,11,8,Double.MAX_VALUE,Double.MAX_VALUE,9,20}
                },
                //工件5
                {
                        {Double.MAX_VALUE,Double.MAX_VALUE,24,10,16,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,18,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,19}
                },
                //工件6
                {
                        {Double.MAX_VALUE,20,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,22,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,18,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,19,Double.MAX_VALUE},
                        {19,Double.MAX_VALUE,17,16,16,Double.MAX_VALUE,Double.MAX_VALUE,18}
                },
                //工件7
                {
                        {Double.MAX_VALUE,Double.MAX_VALUE,16,Double.MAX_VALUE,17,16,Double.MAX_VALUE,Double.MAX_VALUE},
                        {22,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,20,22,Double.MAX_VALUE,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,18,23,Double.MAX_VALUE,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,10,Double.MAX_VALUE,24}
                },
                //工件8
                {
                        {11,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,21,21},
                        {25,11,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE},
                        {24,18,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,25,Double.MAX_VALUE,20}
                }
        };

    }
    //作业结构类
    class shopStructure implements Cloneable{
        //工件编号
        int workId;
        //工序编号
        int workProcess;
        //设备编号
        int deviceId;
        //开始时间
        double startTime;
        //结束时间
        double endTime;
        //拷贝方法
        @Override
        public shopStructure clone() {
            shopStructure shop = null;
            try {
                shop = (shopStructure) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return shop;
        }
    }
    //定义类属性
    //数据
    Data data;
    //工件
    shopStructure shop;
    //工序
    ArrayList<ArrayList<Integer>>workProcess;
    //调度结果
    ArrayList<ArrayList<shopStructure>>workResult;
    //记录工序的开始时间
    double[][] startTime;
    //记录工序的结束时间
    double[][] endTime;
    private FlexibleShopSchedulingProblemDemo(){
        //初始化实验数据
        data=new Data();
        //初始化工件类
        shop=new shopStructure();
        //初始化工序
        workProcess=new ArrayList<ArrayList<Integer>>();
        for(int i=0;i<data.workProcess.length;i++){
            ArrayList<Integer>workProcessTemp=new ArrayList<Integer>();
            for(int j=0;j<data.workProcess[i].length;j++){
                workProcessTemp.add(data.workProcess[i][j]);
            }
            workProcess.add(workProcessTemp);
        }
        //初始化调度结果
        workResult=new ArrayList<ArrayList<shopStructure>>();
        for(int i=0;i<data.deviceId.length;i++){
            ArrayList<shopStructure> workResultTemp=new ArrayList<shopStructure>();
            workResult.add(workResultTemp);
        }
        //初始化工序的开始时间和结束时间
        startTime=new double[data.workProcess.length][6];
        endTime=new double[data.workProcess.length][6];
        for(int i=0;i<startTime.length;i++){
            for(int j=0;j<6;j++){
                startTime[i][j]=-1;
                endTime[i][j]=-1;
            }
        }
    }
    /**
     * 启发策略-最早开始时间
     */
    private void EST(){
        while (true) {
            //遍历工件
            for (int i = 0; i < data.workId.length; i++) {
                //选取工序i的剩余第一个工序
                if (workProcess.get(i).size() != 0) {
                    //System.out.println("=======工件"+(i+1)+"|工序"+workProcess.get(i).get(0)+"=======");
                    //随机生成设备遍历顺序
                    int[] orderArr = new int[data.deviceId.length];
                    for (int j = 0; j < data.deviceId.length; j++) {
                        orderArr[j] = j;
                    }
                    shuffle(orderArr);
                    //找到开始时间最短的机器设备
                    int deviceIndex = data.deviceId.length;
                    double deviceTime = Double.MAX_VALUE;
                    //随机遍历机器
                    for (int j = 0; j < data.deviceId.length; j++) {
                        //工序只能在可加工设备上进行加工
                        if (data.ProcessTime[i][workProcess.get(i).get(0) - 1][orderArr[j]] != Double.MAX_VALUE) {
                            if (workResult.get(orderArr[j]).size() == 0) {
                                //下一工序的开始时间大于等于上一工序的结束时间
                                if (workProcess.get(i).get(0) == 1) {
                                    //第一个工序
                                    deviceTime = 0;
                                    deviceIndex = orderArr[j];
                                    //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                                    break;
                                }
                            } else {
                                if (workProcess.get(i).get(0) == 1) {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < deviceTime) {
                                        deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1;
                                        deviceIndex = orderArr[j];
                                    }
                                } else {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < deviceTime) {
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime >= endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1;
                                            deviceIndex = orderArr[j];
                                        }
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = endTime[i][workProcess.get(i).get(0) - 2]+1;
                                            deviceIndex = orderArr[j];
                                        }
                                    }
                                }
                            }
                        }
                        //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                    }
                    //更新工件信息
                    if (deviceIndex != data.deviceId.length) {
                        shop.workId = i + 1;
                        shop.workProcess = workProcess.get(i).get(0);
                        shop.deviceId = deviceIndex + 1;
                        shop.startTime = deviceTime;
                        startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                        //System.out.println(i + "\t" + (workProcessTemp.get(i).get(0) - 1) + "\t" + deviceIndex);
                        shop.endTime = shop.startTime + data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];
                        endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                        //工件信息加入结果列表
                        workResult.get(deviceIndex).add(shop.clone());
                        //删除该工序
                        workProcess.get(i).remove(0);
                    }
                }
            }
            //所有工序分配完毕时，结束
            boolean stopCondition = true;
            for (int i = 0; i < data.workId.length; i++) {
                if (workProcess.get(i).size() != 0) {
                    stopCondition = false;
                    break;
                }
            }
            if (stopCondition) {
                break;
            }
        }
        printResult(workResult,endTime);
    }
    /**
     * 启发策略-最早结束时间
     * 随机分配工序
     */
    private void EET(){
        while (true) {
            //遍历工件
            for (int i = 0; i < data.workId.length; i++) {
                //选取工序i的剩余第一个工序
                if (workProcess.get(i).size() != 0) {
                    //System.out.println("=======工件"+(i+1)+"|工序"+workProcess.get(i).get(0)+"=======");
                    //随机生成设备遍历顺序
                    int[] orderArr = new int[data.deviceId.length];
                    for (int j = 0; j < data.deviceId.length; j++) {
                        orderArr[j] = j;
                    }
                    shuffle(orderArr);
                    //找到开始时间最短的机器设备
                    int deviceIndex = data.deviceId.length;
                    double deviceTime = Double.MAX_VALUE;
                    //随机遍历机器
                    for (int j = 0; j < data.deviceId.length; j++) {
                        //工序只能在可加工设备上进行加工
                        if (data.ProcessTime[i][workProcess.get(i).get(0) - 1][orderArr[j]] != Double.MAX_VALUE) {
                            if (workResult.get(orderArr[j]).size() == 0) {
                                //下一工序的开始时间大于等于上一工序的结束时间
                                if (workProcess.get(i).get(0) == 1) {
                                    //第一个工序
                                    deviceTime = data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                    deviceIndex = orderArr[j];
                                    break;
                                }
                            } else {
                                if (workProcess.get(i).get(0) == 1) {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]] < deviceTime) {
                                        deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                        deviceIndex = orderArr[j];
                                    }
                                } else {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]]< deviceTime) {
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime >= endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                            deviceIndex = orderArr[j];
                                        }
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = endTime[i][workProcess.get(i).get(0) - 2]+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                            deviceIndex = orderArr[j];
                                        }
                                    }
                                }
                            }
                        }
                        //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                    }
                    //更新工件信息
                    if (deviceIndex != data.deviceId.length) {
                        shop.workId = i + 1;
                        shop.workProcess = workProcess.get(i).get(0);
                        shop.deviceId = deviceIndex + 1;
                        shop.endTime = deviceTime;
                        endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                        shop.startTime = shop.endTime - data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];;
                        startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                        //工件信息加入结果列表
                        workResult.get(deviceIndex).add(shop.clone());
                        //删除该工序
                        workProcess.get(i).remove(0);
                    }
                }
            }
            //所有工序分配完毕时，结束
            boolean stopCondition = true;
            for (int i = 0; i < data.workId.length; i++) {
                if (workProcess.get(i).size() != 0) {
                    stopCondition = false;
                    break;
                }
            }
            if (stopCondition) {
                break;
            }
        }
        printResult(workResult,endTime);
    }
    /**
     * 贪婪启发策略-最早开始时间
     * 匈牙利算法分配第一工序和设备
     */
    private void GEST(){
        //第一个工序分配
        int[] assignResult=shopAssign(data.ProcessTime);
        //分配第一个工序
        for(int i=0;i<assignResult.length;i++){
            int deviceIndex = data.deviceId.length;
            double deviceTime = Double.MAX_VALUE;
            if (workProcess.get(i).size() != 0){
                if (workResult.get(assignResult[i]-1).size() == 0){
                    deviceTime = 0;
                    deviceIndex = assignResult[i]-1;
                }
                else{
                    deviceTime = workResult.get(assignResult[i]-1).get(workResult.get(assignResult[i]-1).size() - 1).endTime+1;
                    deviceIndex = assignResult[i]-1;
                }
            }
            if (deviceIndex != data.deviceId.length) {
                shop.workId = i + 1;
                shop.workProcess = workProcess.get(i).get(0);
                shop.deviceId = deviceIndex + 1;
                shop.startTime = deviceTime;
                startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                shop.endTime = shop.startTime + data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];
                endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                workResult.get(deviceIndex).add(shop.clone());
                workProcess.get(i).remove(0);
            }
        }
        //分配其余工序
        while (true) {
            //遍历工件
            for (int i = 0; i < data.workId.length; i++) {
                //选取工序i的剩余第一个工序
                if (workProcess.get(i).size() != 0) {
                    //System.out.println("=======工件"+(i+1)+"|工序"+workProcess.get(i).get(0)+"=======");
                    //随机生成设备遍历顺序
                    int[] orderArr = new int[data.deviceId.length];
                    for (int j = 0; j < data.deviceId.length; j++) {
                        orderArr[j] = j;
                    }
                    shuffle(orderArr);
                    //找到开始时间最短的机器设备
                    int deviceIndex = data.deviceId.length;
                    double deviceTime = Double.MAX_VALUE;
                    //随机遍历机器
                    for (int j = 0; j < data.deviceId.length; j++) {
                        //工序只能在可加工设备上进行加工
                        if (data.ProcessTime[i][workProcess.get(i).get(0) - 1][orderArr[j]] != Double.MAX_VALUE) {
                            if (workResult.get(orderArr[j]).size() == 0) {
                                //下一工序的开始时间大于等于上一工序的结束时间
                                if (workProcess.get(i).get(0) == 1) {
                                    //第一个工序
                                    deviceTime = 0;
                                    deviceIndex = orderArr[j];
                                    //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                                    break;
                                }
                            } else {
                                if (workProcess.get(i).get(0) == 1) {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < deviceTime) {
                                        deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1;
                                        deviceIndex = orderArr[j];
                                    }
                                } else {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < deviceTime) {
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime >= endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1;
                                            deviceIndex = orderArr[j];
                                        }
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = endTime[i][workProcess.get(i).get(0) - 2]+1;
                                            deviceIndex = orderArr[j];
                                        }
                                    }
                                }
                            }
                        }
                        //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                    }
                    //更新工件信息
                    if (deviceIndex != data.deviceId.length) {
                        shop.workId = i + 1;
                        shop.workProcess = workProcess.get(i).get(0);
                        shop.deviceId = deviceIndex + 1;
                        shop.startTime = deviceTime;
                        startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                        //System.out.println(i + "\t" + (workProcessTemp.get(i).get(0) - 1) + "\t" + deviceIndex);
                        shop.endTime = shop.startTime + data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];
                        endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                        //工件信息加入结果列表
                        workResult.get(deviceIndex).add(shop.clone());
                        //删除该工序
                        workProcess.get(i).remove(0);
                    }
                }
            }
            //所有工序分配完毕时，结束
            boolean stopCondition = true;
            for (int i = 0; i < data.workId.length; i++) {
                if (workProcess.get(i).size() != 0) {
                    stopCondition = false;
                    break;
                }
            }
            if (stopCondition) {
                break;
            }
        }
        printResult(workResult,endTime);
    }
    /**
     * 贪婪启发策略-最早结束时间
     * 匈牙利算法分配第一工序和设备
     */
    private void GEET(){
        //第一个工序分配
        int[] assignResult=shopAssign(data.ProcessTime);
        //分配第一个工序
        for(int i=0;i<assignResult.length;i++){
            int deviceIndex = data.deviceId.length;
            double deviceTime = Double.MAX_VALUE;
            if (workProcess.get(i).size() != 0){
                if (workResult.get(assignResult[i]-1).size() == 0){
                    deviceTime = data.ProcessTime[i][workProcess.get(i).get(0)-1][assignResult[i]-1];
                    deviceIndex = assignResult[i]-1;
                }
                else{
                    deviceTime = workResult.get(assignResult[i]-1).get(workResult.get(assignResult[i]-1).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][assignResult[i]-1];
                    deviceIndex = assignResult[i]-1;
                }
            }
            if (deviceIndex != data.deviceId.length) {
                shop.workId = i + 1;
                shop.workProcess = workProcess.get(i).get(0);
                shop.deviceId = deviceIndex + 1;
                shop.endTime = deviceTime;
                endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                shop.startTime = shop.endTime - data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];;
                startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                workResult.get(deviceIndex).add(shop.clone());
                workProcess.get(i).remove(0);
            }
        }
        //分配其余工序
        while (true) {
            //遍历工件
            for (int i = 0; i < data.workId.length; i++) {
                //选取工序i的剩余第一个工序
                if (workProcess.get(i).size() != 0) {
                    //System.out.println("=======工件"+(i+1)+"|工序"+workProcess.get(i).get(0)+"=======");
                    //随机生成设备遍历顺序
                    int[] orderArr = new int[data.deviceId.length];
                    for (int j = 0; j < data.deviceId.length; j++) {
                        orderArr[j] = j;
                    }
                    shuffle(orderArr);
                    //找到开始时间最短的机器设备
                    int deviceIndex = data.deviceId.length;
                    double deviceTime = Double.MAX_VALUE;
                    //随机遍历机器
                    for (int j = 0; j < data.deviceId.length; j++) {
                        //工序只能在可加工设备上进行加工
                        if (data.ProcessTime[i][workProcess.get(i).get(0) - 1][orderArr[j]] != Double.MAX_VALUE) {
                            if (workResult.get(orderArr[j]).size() == 0) {
                                //下一工序的开始时间大于等于上一工序的结束时间
                                if (workProcess.get(i).get(0) == 1) {
                                    //第一个工序
                                    deviceTime = data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                    deviceIndex = orderArr[j];
                                    break;
                                }
                            } else {
                                if (workProcess.get(i).get(0) == 1) {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]] < deviceTime) {
                                        deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                        deviceIndex = orderArr[j];
                                    }
                                } else {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]]< deviceTime) {
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime >= endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                            deviceIndex = orderArr[j];
                                        }
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = endTime[i][workProcess.get(i).get(0) - 2]+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                            deviceIndex = orderArr[j];
                                        }
                                    }
                                }
                            }
                        }
                        //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                    }
                    //更新工件信息
                    if (deviceIndex != data.deviceId.length) {
                        shop.workId = i + 1;
                        shop.workProcess = workProcess.get(i).get(0);
                        shop.deviceId = deviceIndex + 1;
                        shop.endTime = deviceTime;
                        endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                        shop.startTime = shop.endTime - data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];;
                        startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                        //工件信息加入结果列表
                        workResult.get(deviceIndex).add(shop.clone());
                        //删除该工序
                        workProcess.get(i).remove(0);
                    }
                }
            }
            //所有工序分配完毕时，结束
            boolean stopCondition = true;
            for (int i = 0; i < data.workId.length; i++) {
                if (workProcess.get(i).size() != 0) {
                    stopCondition = false;
                    break;
                }
            }
            if (stopCondition) {
                break;
            }
        }
        printResult(workResult,endTime);
    }
    private int[] shopAssign(double[][][] ProcessTime){
        int[] assignResult=new int[data.workId.length];
        double[][] fistProcess=new double[data.workId.length][data.deviceId.length];
        for(int i=0;i<data.workId.length;i++){
            for(int j=0;j<data.deviceId.length;j++){
                fistProcess[i][j]=ProcessTime[i][0][j];
            }
        }
        //匈牙利算法分配第一工序
        HungarianAlgorithm lp2=new HungarianAlgorithm(fistProcess);
        lp2.hungarianMethod();
        int N = lp2.m.length;
        double finalAnswer = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (lp2.m[i][j] == -1) {
                    assignResult[i]=j+1;
                }
            }
        }
//        System.out.println("=================");
//        for(int i=0;i<assignResult.length;i++){
//            System.out.print(assignResult[i]+"\t");
//        }
        return assignResult;
    }
    //数组元素交换
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    //数组随机打乱
    private static void shuffle(int[] arr) {
        Random rand = new Random();
        for (int i = arr.length; i > 0; i--) {
            int randInd = rand.nextInt(i);
            swap(arr, randInd, i - 1);
        }
    }
    //打印结果
    private void printResult(ArrayList<ArrayList<shopStructure>>workResult,double[][] endTime){
        for(int i=0;i<workResult.size();i++){
            System.out.println("=======设备"+(i+1)+"=======");
            for(int j=0;j<workResult.get(i).size();j++){
                System.out.println("工件"+workResult.get(i).get(j).workId+"\t"+"工序"+workResult.get(i).get(j).workProcess);
                System.out.println(workResult.get(i).get(j).startTime+"\t"+workResult.get(i).get(j).endTime+"\t"+data.ProcessTime[workResult.get(i).get(j).workId-1][workResult.get(i).get(j).workProcess-1][workResult.get(i).get(j).deviceId-1]);
            }
        }
        double[] endTimeList=new double[data.workId.length];
        for(int i=0;i<endTime.length;i++){
            endTimeList[i]=getMax(endTime[i]);
            System.out.println("工件"+(i+1)+"完工时间："+endTimeList[i]);
        }
        System.out.println("最大完工时间："+getMax(endTimeList));
    }
    //获取数组的最大值
    private double getMax(double[] arr) {
        double max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (max < arr[i]) {
                max = arr[i];
            }
        }
        return max;
    }
    //遗传算法求解
    GAAlgorithm ga=new GAAlgorithm();
    class GAAlgorithm{
        Data data;
        //工件
        shopStructure shop;
        //种群个数
        int popNum;
        //工序个数
        int processNum;
        //迭代次数
        int ITER;
        //工序列表
        ArrayList<Integer>processList;
        //工序编码
        int[][] processCode;
        //设备编码
        int[][] deviceCode;
        //工序可用设备编码
        ArrayList<ArrayList<ArrayList<Integer>>> processDevice;
        //最优工序个体
        int[] processBest;
        //最优设备个体
        int[] deviceBest;
        //最优目标值
        double targetBest;
        //调度结果
        ArrayList<ArrayList<shopStructure>>workResult;
        private GAAlgorithm(){
            data=new Data();
            //初始化工件类
            shop=new shopStructure();
            //种群个数
            popNum=200;
            processNum=0;
            ITER=500;
            for(int i=0;i<data.workProcess.length;i++){
                processNum+=data.workProcess[i].length;
            }
            //工序编码
            processCode=new int[popNum][processNum];
            processList=new ArrayList<>();
            for(int i=0;i<data.workProcess.length;i++){
                for(int j=0;j<data.workProcess[i].length;j++){
                    processList.add(i+1);
                }
            }
            //设备编码
            deviceCode=new int[popNum][processNum];
            //工序可用设备编码
            processDevice=new ArrayList<>();
            for(int i=0;i<data.ProcessTime.length;i++){
                ArrayList<ArrayList<Integer>>processDevice1=new ArrayList<>();
                for(int j=0;j<data.ProcessTime[i].length;j++){
                    double[] deviceTimeList=new double[data.deviceId.length];
                    int[] deviceTimeIndex=new int[data.deviceId.length];
                    for(int k=0;k<data.ProcessTime[i][j].length;k++){
                        deviceTimeList[k]=data.ProcessTime[i][j][k];
                        deviceTimeIndex[k]=k;
                    }
                    //由小到大排序
                    for(int ii=1;ii<deviceTimeList.length;ii++){
                        for(int jj=0;jj<deviceTimeList.length-ii;jj++){
                            if(deviceTimeList[jj]>deviceTimeList[jj+1]){
                                double temp=deviceTimeList[jj+1];
                                int tempIndex=deviceTimeIndex[jj+1];
                                deviceTimeList[jj+1]=deviceTimeList[jj];
                                deviceTimeIndex[jj+1]=deviceTimeIndex[jj];
                                deviceTimeList[jj]=temp;
                                deviceTimeIndex[jj]=tempIndex;
                            }
                        }
                    }
                    ArrayList<Integer>processDevice2=new ArrayList<>();
                    for(int k=0;k<data.ProcessTime[i][j].length;k++){
                        if(deviceTimeList[k]!=Double.MAX_VALUE){
                            processDevice2.add(deviceTimeIndex[k]+1);
                        }
                    }
                    processDevice1.add(processDevice2);
                }
                processDevice.add(processDevice1);
            }
            //最优工序个体
            processBest=new int[processNum];
            //最优设备个体
            deviceBest=new int[processNum];
            //最优目标值
            targetBest=Double.MAX_VALUE;
            //初始化调度结果
            workResult=new ArrayList<ArrayList<shopStructure>>();
            for(int i=0;i<data.deviceId.length;i++){
                ArrayList<shopStructure> workResultTemp=new ArrayList<shopStructure>();
                workResult.add(workResultTemp);
            }
        }
        //种群初始化-个体编码
        private void initPop(){
            for(int i=0;i<popNum;i++){
                int[] processRamdomIndex=processRandom();
                for(int j=0;j<processNum;j++){
                    processCode[i][j]=processList.get(processRamdomIndex[j]-1);
                }
                int[] deviceRandomIndex=deviceRandom(processCode[i]);
                for(int j=0;j<processNum;j++){
                    deviceCode[i][j]=deviceRandomIndex[j];
                }
            }
        }
        //初始最优个体与适应值
        private void initFitness(){
            for(int i=0;i<processCode.length;i++){
                double temp=fitnessPop(processCode[i],deviceCode[i],false);
                if(temp<targetBest){
                    targetBest=temp;
                    processBest=processCode[i].clone();
                    deviceBest=deviceCode[i].clone();
                }
            }
        }
        //个体变异：工序随机交换指定偶数位；设备随机选取
        private int[][] mutationPop(int[] processPop,int[] devicePop,boolean mutateType){
            int[][] newPop=new int[2][processPop.length];
            newPop[0]=processPop.clone();
            newPop[1]=devicePop.clone();
            Random random = new Random();
            int mutateNum;
            int[] processIndex;
            //交叉前变异
            if(mutateType){
                mutateNum=2;
                processIndex=new int[mutateNum];
            }
            //交叉后变异
            else{
                //变异位数
                mutateNum=processPop.length/5;
                if(mutateNum%2!=0) {
                    mutateNum-=1;
                }
                processIndex=new int[mutateNum];
            }
            //工序变异位置
            for(int i=0;i<processIndex.length;i++){
                processIndex[i]=random.nextInt(newPop[0].length);
            }
//            System.out.println();
//            System.out.println("工序变异位置");
//            for(int i=0;i<processIndex.length;i++){
//                System.out.print((processIndex[i]+1)+"\t");
//            }
//            System.out.println();
            //工序变异
            for(int i=0;i<processIndex.length;i+=2){
                int temp=newPop[0][processIndex[i]];
                newPop[0][processIndex[i]]=newPop[0][processIndex[i+1]];
                newPop[0][processIndex[i+1]]=temp;
            }
//            System.out.println("工序变异结果");
//            for(int j=0;j<newPop[0].length;j++){
//                System.out.print(newPop[0][j]+"\t");
//            }
//            System.out.println();
            //设备变异位置
            for(int i=0;i<processIndex.length;i++){
                //确定工序
                int count=0;
                for(int j=0;j<newPop[0].length;j++){
                    if(newPop[0][j]==newPop[0][processIndex[i]]){
                        newPop[1][j]=random.nextInt(processDevice.get(newPop[0][processIndex[i]]-1).get(count).size());
                        count+=1;
                    }
                }
            }
//            System.out.println("设备变异结果");
//            for(int i=0;i<newPop[1].length;i++){
//                System.out.print(newPop[1][i]+"\t");
//            }
            return newPop;
        }
        //轮盘赌策略-计算选择概率
        private double[] rouletteStrategy(int[][] processCodeChild,int[][] deviceCodeChild){
            double[] selectedProb=new double[popNum];
            double fitnessSum=0;
            //计算个体适应值
            for(int i=0;i<popNum;i++){
                selectedProb[i]=fitnessPop(processCodeChild[i],deviceCodeChild[i],false);
                fitnessSum+=selectedProb[i];
            }
            //反差
            double selectedSum=0;
            for(int i=0;i<popNum;i++){
                selectedProb[i]=fitnessSum-selectedProb[i];
                selectedSum+=selectedProb[i];
            }
            //计算累积概率
            double[] selectedProbTemp=selectedProb.clone();
            for(int i=0;i<popNum;i++){
                if(i==0) {
                    selectedProb[i]=selectedProbTemp[i]/selectedSum;
                } else {
                    selectedProb[i]=(selectedProbTemp[i]/selectedSum)+selectedProb[i-1];
                }
            }
            return selectedProb;
        }
        //个体选择
        private int[] selectPop(double[] selectedProb){
            int[] selectedIndex=new int[2];
            Random random = new Random();
            for(int i=0;i<selectedIndex.length;i++){
                double temp=random.nextDouble();
                //找到位置索引
                for(int j=0;j<selectedProb.length;j++){
                    if(temp<=selectedProb[j]){
                        selectedIndex[i]=j;
                        break;
                    }
                }
            }
            return selectedIndex;
        }
        //个体交叉
        private int[][] crossPop(int[] selectedIndex){
            int[][] crossResult=new int[4][processNum];
            //随机交叉位置
            int crossNum=processNum/5;
            if(crossNum<=0) {
                crossNum=1;
            }
            int[] crossIndex=new int[crossNum];
            int[] arrTemp=new int[processNum];
            for(int i=0;i<arrTemp.length;i++){
                arrTemp[i]=i;
            }
            shuffle(arrTemp);
            for(int i=0;i<crossIndex.length;i++){
                crossIndex[i]=arrTemp[i];
            }
//            System.out.println("个体1交叉位置：");
//            for(int i=0;i<crossIndex.length;i++){
//                System.out.print(crossIndex[i]+"\t");
//            }
//            System.out.println();
            //找到另一个体上的交叉位置
            int[] anotherCrossIndex=new int[crossNum];
            for(int i=0;i<anotherCrossIndex.length;i++){anotherCrossIndex[i]=-1;}
            for(int i=0;i<crossIndex.length;i++){
                for(int j=0;j<processCode[selectedIndex[1]].length;j++){
                    if(processCode[selectedIndex[1]][j]==processCode[selectedIndex[0]][crossIndex[i]]){
                        //是否已经添加过
                        boolean appendType=false;
                        for(int k=0;k<anotherCrossIndex.length;k++){
                            if(anotherCrossIndex[k]==j){
                                appendType=true;
                                break;
                            }
                        }
                        if(!appendType){
                            anotherCrossIndex[i]=j;
                        }
                    }
                }
            }
//            System.out.println("个体2交叉位置：");
//            for(int i=0;i<anotherCrossIndex.length;i++){
//                System.out.print(anotherCrossIndex[i]+"\t");
//            }
//            System.out.println();
            //交叉
            for(int i=0;i<selectedIndex.length;i++){
                int count=0;
                for(int j=0;j<processNum;j++){
                    if(i==0){
                        if(judgeExist(crossIndex,j)){
                            crossResult[0][j]=processCode[selectedIndex[i]][j];
                            crossResult[2][j]=deviceCode[selectedIndex[i]][j];
                        }
                        else{
                            //另一个体当中选择第一个不在交叉位置的索引
                            while(true){
                                if(!judgeExist(anotherCrossIndex,count)){
                                    break;
                                }
                                else{
                                    count++;
                                }
                            }
                            crossResult[0][j]=processCode[selectedIndex[1-i]][count];
                            crossResult[2][j]=deviceCode[selectedIndex[1-i]][count];
                            count++;
                        }
                    }
                    else{
                        if(judgeExist(anotherCrossIndex,j)){
                            crossResult[1][j]=processCode[selectedIndex[i]][j];
                            crossResult[3][j]=deviceCode[selectedIndex[i]][j];
                        }
                        else{
                            //另一个体当中选择第一个不在交叉位置的索引
                            while(true){
                                if(!judgeExist(crossIndex,count)){
                                    break;
                                }
                                else{
                                    count++;
                                }
                            }
                            crossResult[1][j]=processCode[selectedIndex[1-i]][count];
                            crossResult[3][j]=deviceCode[selectedIndex[1-i]][count];
                            count++;
                        }
                    }
                }
            }
            //校验设备分配-如果超出索引，则随机分配；
            crossResult[2]=deviceCheck(crossResult[0].clone(),crossResult[2].clone());
            crossResult[3]=deviceCheck(crossResult[1].clone(),crossResult[3].clone());
            return crossResult;
        }
        //子代生成
        private void generateChild(int[][] processCode,int[][] deviceCode,int[][] processCodeChild,int[][] deviceCodeChild){
            //计算原种群适应值
            double[] targetValue=new double[processCode.length+processCodeChild.length];
            int[] targetIndex=new int[processCode.length+processCodeChild.length];
            for(int i=0;i<processCode.length;i++){
                targetValue[i]=fitnessPop(processCode[i].clone(),deviceCode[i].clone(),false);
                targetIndex[i]=i;
            }
            for(int i=0;i<processCodeChild.length;i++){
                targetValue[i+processCode.length]=fitnessPop(processCodeChild[i].clone(),deviceCodeChild[i].clone(),false);
                targetIndex[i+processCode.length]=i+processCode.length;
            }
            //排序
            for(int i=1;i<targetValue.length;i++){
                for(int j=0;j<targetValue.length-i;j++){
                    if(targetValue[j]>targetValue[j+1]){
                        double temp=targetValue[j+1];
                        int tempIndex=targetIndex[j+1];
                        targetValue[j+1]=targetValue[j];
                        targetIndex[j+1]=targetIndex[j];
                        targetValue[j]=temp;
                        targetIndex[j]=tempIndex;
                    }
                }
            }
            //生成子代
            for(int i=0;i<this.processCode.length;i++){
                if(targetIndex[i]<processCode.length){
                    this.processCode[i]=processCode[targetIndex[i]].clone();
                    this.deviceCode[i]=deviceCode[targetIndex[i]].clone();
                }
                else{
                    this.processCode[i]=processCodeChild[targetIndex[i]-processCode.length].clone();
                    this.deviceCode[i]=deviceCodeChild[targetIndex[i]-processCode.length].clone();
                }
            }
        }
        //更新最优个体与适应值
        private void updateFitness(){
            for(int i=0;i<processCode.length;i++){
                double temp=fitnessPop(processCode[i],deviceCode[i],false);
                if(temp<targetBest){
                    targetBest=temp;
                    processBest=processCode[i].clone();
                    deviceBest=deviceCode[i].clone();
                }
            }
        }
        //判断是否存在
        private boolean judgeExist(int[] arr,int element){
            boolean existType=false;
            for(int i=0;i<arr.length;i++){
                if(arr[i]==element){
                    existType=true;
                    break;
                }
            }
            return existType;
        }
        //计算个体适应值
        private double fitnessPop(int[] processPop,int[] devicePop,boolean rstType){
            double fitness=0;
            fitness=greedyDecode(processPop,devicePop,rstType);
            return fitness;
        }
        //个体解码
        private double greedyDecode(int[] processPop,int[] devicePop,boolean rstType){
            //清空列表
            workResult=new ArrayList<ArrayList<shopStructure>>();
            for(int i=0;i<data.deviceId.length;i++){
                ArrayList<shopStructure> workResultTemp=new ArrayList<shopStructure>();
                workResult.add(workResultTemp);
            }
//            System.out.println("解码个体");
//            for(int i=0;i<processPop.length;i++){
//                System.out.print(processPop[i]+"\t");
//            }
//            System.out.println();
//            for(int i=0;i<devicePop.length;i++){
//                System.out.print(devicePop[i]+"\t");
//            }
//            System.out.println();
            //记录开始和结束时间
            double[][] startTime=new double[data.workId.length][6];
            double[][] endTime=new double[data.workId.length][6];
            for(int i=0;i<startTime.length;i++){
                for(int j=0;j<6;j++){
                    startTime[i][j]=-1;
                    endTime[i][j]=-1;
                }
            }
            //记录工件使用次数-工序
            int[] workList=new int[data.workId.length];
            //分配工序到设备
            for(int i=0;i<processPop.length;i++){
//                System.out.println("工序：");
//                for(int ii=0;ii<workList.length;ii++){
//                    System.out.print(workList[ii]+"\t");
//                }
//                System.out.println();
                int deviceIndex=processDevice.get(processPop[i]-1).get(workList[processPop[i]-1]).get(devicePop[i]);
                double deviceTime = Double.MAX_VALUE;
                if(workResult.get(deviceIndex-1).size()==0){
                    if(workList[processPop[i]-1]==0){
                        deviceTime=0;
                    }
                    else{
                        deviceTime = endTime[processPop[i]-1][workList[processPop[i]-1]-1]+1;
                    }
                }
                else{
                    //设备已存在分配的工序
                    //第一个工序分配
                    if(workList[processPop[i]-1]==0){
                        deviceTime=workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime+1;
                    }
                    else{
                        if (workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime >= endTime[processPop[i]-1][workList[processPop[i]-1]-1]) {
                            deviceTime = workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime+1;
                        }
                        if (workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime < endTime[processPop[i]-1][workList[processPop[i]-1]-1]) {
                            deviceTime = endTime[processPop[i]-1][workList[processPop[i]-1]-1]+1;
                        }
                    }
                }
                //更新工件信息
                shop.workId = processPop[i];
                shop.workProcess = workList[processPop[i]-1]+1;
                shop.deviceId = deviceIndex;
                shop.startTime = deviceTime;
                startTime[processPop[i]-1][workList[processPop[i]-1]] = shop.startTime;
                shop.endTime = shop.startTime + data.ProcessTime[processPop[i]-1][workList[processPop[i]-1]][deviceIndex-1];
                endTime[processPop[i]-1][workList[processPop[i]-1]] = shop.endTime;
                workResult.get(deviceIndex-1).add(shop.clone());
                workList[processPop[i]-1]+=1;

            }
            //计算最大完工时间
            double maxTime=maxEndTime(endTime);
            if(rstType) {
                printResult(workResult,endTime);
            }
            return maxTime;
        }
        //计算最大完工时间
        private double maxEndTime(double[][] endTime){
            double maxTime=0;
            double[] endTimeList=new double[data.workId.length];
            for(int i=0;i<endTime.length;i++){
                endTimeList[i]=getMax(endTime[i]);
            }
            maxTime=getMax(endTimeList);
            return maxTime;
        }
        //产生个体长度的随机数-工序
        private int[] processRandom(){
            int[] processSort=new int[processNum];
            double[] processRandomValue=new double[processNum];
            Random random = new Random();
            for(int i=0;i<processNum;i++){
                processRandomValue[i]=random.nextDouble()+9;
                processSort[i]=i+1;
            }
            //冒泡排序
            for(int i=1;i<processRandomValue.length;i++){
                for(int j=0;j<processRandomValue.length-i;j++){
                    if(processRandomValue[j]>processRandomValue[j+1]){
                        double temp=processRandomValue[j+1];
                        int tempIndex=processSort[j+1];
                        processRandomValue[j+1]=processRandomValue[j];
                        processSort[j+1]=processSort[j];
                        processRandomValue[j]=temp;
                        processSort[j]=tempIndex;
                    }
                }
            }
            return processSort;
        }
        //产生个体长度的随机数-设备索引
        private int[] deviceRandom(int[] processIndex){
            int[] deviceRandomValue=new int[processNum];
            int[] workList=new int[data.workId.length];
            Random random = new Random();
            int count=0;
            for(int i=0;i<processNum;i++){
                deviceRandomValue[count++]=random.nextInt(processDevice.get(processIndex[i]-1).get(workList[processIndex[i]-1]).size());
                workList[processIndex[i]-1]+=1;
            }
            return deviceRandomValue;
        }
        //设备校验
        private int[] deviceCheck(int[] processPop,int[] devicePop){
            int[] checkResult=new int[devicePop.length];
            int[] workList=new int[data.workId.length];
            Random random = new Random();
            for(int i=0;i<devicePop.length;i++){
                int deviceIndexLength=processDevice.get(processPop[i]-1).get(workList[processPop[i]-1]).size();
                if(devicePop[i]<deviceIndexLength){
                    checkResult[i]=devicePop[i];
                }
                else{
                    checkResult[i]=random.nextInt(deviceIndexLength);
                }
                workList[processPop[i]-1]+=1;
            }
            return checkResult;
        }
        private void GAMethod(){
            //初始化
            initPop();
//            System.out.println("初始种群：");
//            for(int i=0;i<popNum;i++){
//                System.out.println("个体"+(i+1)+"工序编码");
//                for(int j=0;j<processNum;j++){
//                    System.out.print(processCode[i][j]+"\t");
//                }
//                System.out.println();
//                System.out.println("个体"+(i+1)+"设备编码索引");
//                for(int j=0;j<processNum;j++){
//                    System.out.print(deviceCode[i][j]+"\t");
//                }
//                System.out.println();
//            }
            //初始最优个体与适应值
            initFitness();
            System.out.println("最优个体："+targetBest);
            for(int i=0;i<processNum;i++){
                System.out.print(processBest[i]+"\t");
            }
            System.out.println();
            for(int i=0;i<processNum;i++){
                System.out.print(deviceBest[i]+"\t");
            }
            //迭代记录
            int iter=0;
            while(iter<ITER){
//                System.out.println();
//                System.out.println("=========第"+(iter+1)+"次迭代=========");
//                System.out.println("最优值："+targetBest);
                //扰动
                int[][] processCodeTemp=processCode.clone();
                int[][] deviceCodeTemp=deviceCode.clone();
                for(int i=0;i<popNum;i++){
                    int[][] mutatePopTemp=mutationPop(processCode[i].clone(),deviceCode[i].clone(),true);
                    processCodeTemp[i]=mutatePopTemp[0].clone();
                    deviceCodeTemp[i]=mutatePopTemp[1].clone();
                }
//                System.out.println();
//                System.out.println("扰动种群：");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCodeTemp[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCodeTemp[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //交叉
                int[][] processCodeChild=processCode.clone();
                int[][] deviceCodeChild=deviceCode.clone();
                double[] selectedProb=rouletteStrategy(processCodeTemp,deviceCodeTemp);
                int crossCount=0;
                for(int i=0;i<processCode.length/2;i++){
                    int[] selectedIndex= selectPop(selectedProb);
                    int[][] crossResult=crossPop(selectedIndex);
                    processCodeChild[crossCount]=crossResult[0].clone();
                    deviceCodeChild[crossCount]=crossResult[2].clone();
                    crossCount++;
                    processCodeChild[crossCount]=crossResult[1].clone();
                    deviceCodeChild[crossCount]=crossResult[3].clone();
                    crossCount++;
                }
//                System.out.println();
//                System.out.println("交叉种群：");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //变异
                for(int i=0;i<popNum;i++){
                    int[][] mutatePopTemp=mutationPop(processCodeChild[i].clone(),deviceCodeChild[i].clone(),false);
                    processCodeChild[i]=mutatePopTemp[0].clone();
                    deviceCodeChild[i]=mutatePopTemp[1].clone();
                }
//                System.out.println();
//                System.out.println("变异种群：");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //选择
                generateChild(processCode.clone(),deviceCode.clone(),processCodeChild.clone(),deviceCodeChild.clone());
//                System.out.println("子代种群：");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCode[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCode[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //更新
                updateFitness();
                iter++;
            }
            System.out.println("最优个体："+targetBest);
            for(int i=0;i<processNum;i++){
                System.out.print(processBest[i]+"\t");
            }
            System.out.println();
            for(int i=0;i<processNum;i++){
                System.out.print(deviceBest[i]+"\t");
            }
            System.out.println();
            greedyDecode(processBest,deviceBest,true);
        }
    }


    public static void main(String[] args){
        FlexibleShopSchedulingProblemDemo lp=new FlexibleShopSchedulingProblemDemo();
        lp.ga.GAMethod();
        lp.EST();
        lp.GEST();
        lp.EET();
        lp.GEET();
    }
}


